package test

import (
	"context"
	"encoding/xml"
	"fmt"
	"git.skcks.cn/Shikong/go-gb28181/pkg/manscdp"
	"git.skcks.cn/Shikong/go-gb28181/pkg/utils"
	"github.com/emiago/sipgo"
	"github.com/emiago/sipgo/sip"
	"github.com/icholy/digest"
	"os"
	"os/signal"
	"testing"
	"time"
)

func TestClient(t *testing.T) {
	sip.SIPDebug = true

	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)

	ua, _ := sipgo.NewUA(
		sipgo.WithUserAgent("44050100002000000002"),
		sipgo.WithUserAgentHostname("10.10.10.20:5099"))

	srv, _ := sipgo.NewServer(ua) // Creating server handle

	client, _ := sipgo.NewClient(ua,
		sipgo.WithClientPort(5099),
		sipgo.WithClientHostname("10.10.10.20"))

	target := sip.Uri{
		User:    "44050100002000000003",
		Host:    "10.10.10.20",
		Port:    5060,
		Headers: sip.NewParams(),
	}
	uri := sip.Uri{User: "44050100002000000002", Host: "10.10.10.20", Port: 5099}
	req := sip.NewRequest(sip.REGISTER, target)
	req.AppendHeader(sip.NewHeader("X-GB-Ver", "2.0"))
	req.AppendHeader(sip.NewHeader("Contact", uri.String()))
	req.AppendHeader(sip.NewHeader("Expires", "3600"))
	//req.Contact = uasContact

	err := sipgo.ClientRequestBuild(client, req)
	if err != nil {
		t.Fatal(err)
	}

	quit := make(chan os.Signal)

	go func() {
		err = srv.ListenAndServe(ctx, "udp", ":5099")
		if err != nil {
			t.Fatal(err)
		}
	}()

	go func() {
		defer func() {
			cancel()
			quit <- os.Interrupt
		}()

		//for {
		select {
		case <-ctx.Done():
			break
		default:
			tx, err := client.TransactionRequest(ctx, req)

			if err != nil {
				t.Fatal(err)
			}

			resp, err := getResponse(tx)
			tx.Terminate()

			headers := resp.Headers()
			for _, h := range headers {
				t.Log(h.Name(), "=>", h.Value())
			}
			t.Log(resp.String())

			if resp.StatusCode == 401 {
				wwwAuth := resp.GetHeader("WWW-Authenticate")
				chal, err := digest.ParseChallenge(wwwAuth.Value())
				if err != nil {
					t.Fatal(err, "Fail to parse challenge")
				}

				// Reply with digest
				cred, _ := digest.Digest(chal, digest.Options{
					Method:   req.Method.String(),
					Username: "44050100002000000002",
					URI:      target.String(),
					Password: "123456",
				})

				newReq := req.Clone()
				newReq.RemoveHeader("Via") // Must be regenerated by tranport layer
				newReq.AppendHeader(sip.NewHeader("Authorization", cred.String()))

				ctx := context.Background()
				tx, err := client.TransactionRequest(ctx, newReq, sipgo.ClientRequestAddVia)
				if err != nil {
					t.Fatal("Fail to create transaction")
				}

				resp, err = getResponse(tx)
				tx.Terminate()
				if err != nil {
					t.Fatal("Fail to get response")
				}

				if resp.StatusCode != 403 {
					headers := resp.Headers()
					for _, h := range headers {
						t.Log(h.Name(), "=>", h.Value())
					}
					tx.Done()
					//break
				}

				t.Log(resp.String())
			}
		}
		//}
	}()

	signal.Notify(quit, os.Interrupt)
	<-quit
}

func TestCatalog(t *testing.T) {
	ua, _ := sipgo.NewUA(
		sipgo.WithUserAgent("44050100002000000002"),
		sipgo.WithUserAgentHostname("10.10.10.20:5099"))

	client, _ := sipgo.NewClient(ua,
		sipgo.WithClientHostname("10.10.10.20"),
		sipgo.WithClientPort(5099))

	resp := new(manscdp.CatalogResp)
	resp.XMLName = xml.Name{Local: "Response"}
	resp.DeviceID = "44050100002000000002"
	resp.CmdType = "Catalog"
	resp.SumNum = "1"
	resp.DeviceList = new(manscdp.CateLogDeviceList)
	resp.DeviceList.XMLName = xml.Name{Local: "DeviceList"}
	resp.DeviceList.Num = "1"
	resp.DeviceList.Item = make([]manscdp.CateLogDevice, 1)
	resp.DeviceList.Item[0].DeviceID = "44050100002000000002"
	resp.DeviceList.Item[0].Name = "设备名称"
	resp.DeviceList.Item[0].Manufacturer = "设备厂商"
	resp.DeviceList.Item[0].ErrCode = "0"
	resp.DeviceList.Item[0].EndTime = "2022-12-31'T'23:59:59"
	resp.DeviceList.Item[0].Port = fmt.Sprintf("%d", 5099)

	resp.SN = "90000"

	marshal, _ := utils.XMLMarshal(resp, "gb2312")
	t.Logf("回复查询指令: %s\n%+v\n", "Catalog", resp)

	target := sip.Uri{
		User:    "44050100002000000003",
		Host:    "10.10.10.20",
		Port:    5060,
		Headers: sip.NewParams(),
	}

	time.Sleep(time.Second * 1)
	//uri := sip.Uri{User: "44050100002000000002", Host: "10.10.10.20", Port: 5099}
	nReq := sip.NewRequest(sip.MESSAGE, target)
	nReq.SetTransport("UDP")
	//to := sip.NewHeader("To", req.GetHeader("From").Value())
	//from := sip.NewHeader("From", req.GetHeader("To").Value())
	//nReq.AppendHeader(to)
	//nReq.AppendHeader(from)
	//nReq.AppendHeader(req.GetHeader("Call-ID"))
	nReq.AppendHeader(sip.NewHeader("Content-Type", "Application/MANSCDP+xml"))

	nReq.SetBody(marshal)
	err := sipgo.ClientRequestBuild(client, nReq)
	//if err != nil {
	//	logger.Error().Err(err)
	//}

	t.Logf("向服务器发送查询指令: %s\n%+v\n", "Catalog", nReq)

	client.TransactionLayer()
	_, err = client.TransactionRequest(context.Background(), nReq)
	if err != nil {
		t.Fatal(err)
		return
	}
	_ = client.Close()
	t.Log("向服务器发送查询指令完成")
}

func getResponse(tx sip.ClientTransaction) (*sip.Response, error) {
	select {
	case <-tx.Done():
		return nil, fmt.Errorf("transaction died")
	case res := <-tx.Responses():
		return res, nil
	}
}
